package com.github.mzule.abilityrouter.router;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.app.Context;
import ohos.utils.net.Uri;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Created by CaoDongping on 4/6/16.
 */
public class Routers {
    private static final String KEY_RAW_URL = "com.github.mzule.abilityrouter.router.KeyRawUrl";
    private static final List<Mapping> mappings = new ArrayList<>();
    private static final int MARK = 15;
    private static void initIfNeed() {
        if (!mappings.isEmpty()) {
            return;
        }
        RouterInit.init();
        sort();
    }
    /**
     * 调用
     *
     * @param format 格式
     * @param ability 目标ability类
     * @param method 方法
     * @param extraTypes 额外类型
     * */
    public static void map(String format,Class<? extends Ability> ability,
                           MethodInvoker method,ExtraTypes extraTypes) {
        mappings.add(new Mapping(format, ability, method, extraTypes));
    }

    private static void sort() {
        // ensure that user/collection is rank top over user/:userId
        // so scheme://user/collection will match user/collection not user/:userId
        Collections.sort(mappings, new Comparator<Mapping>() {
            @Override
            public int compare(Mapping lhs, Mapping rhs) {
                return lhs.getFormat().compareTo(rhs.getFormat()) * -1;
            }
        });
    }
    /**
     * 通过url打开匹配页面
     *
     * @param context 上下文
     * @param url string类型的地址
     * @return open 通过url打开匹配页面
     * */
    public static boolean open(Context context, String url) {
        return open(context, Uri.parse(url));
    }
    /**
     * 通过路由器回调的方法进行打开匹配页面
     *
     * @param context 上下文
     * @param url string类型的地址
     * @param callback 回调
     * @return  open 通过路由器回调的方法进行打开匹配页面
     * */
    public static boolean open(Context context, String url, RouterCallback callback) {
        return open(context, Uri.parse(url), callback);
    }
    /**
     * 打开匹配的uri页面
     *
     * @param context 上下文
     * @param uri 地址
     * @return open 打开匹配的uri页面
     * */
    public static boolean open(Context context, Uri uri) {
        return open(context, uri, getGlobalCallback(context));
    }
    /**
     * 打开匹配Uri的页面并回调
     *
     * @param context 上下文
     * @param uri 地址
     * @param callback 回调
     * @return open 打开匹配Uri的页面并回调
     * */
    public static boolean open(Context context, Uri uri, RouterCallback callback) {
        return open(context, uri, -1, callback);
    }
    /**
     * 通过目标url打开指定ability并回调
     *
     * @param ability 目标ability
     * @param url 地址
     * @param requestCode 请求代码的参数
     * @return openForResult 通过目标url打开指定ability并回调
     * */
    public static boolean openForResult(Ability ability, String url, int requestCode) {
        return openForResult(ability, Uri.parse(url), requestCode);
    }
    /**
     * 通过url打开匹配的页面并回调
     *
     * @param ability 目标ability
     * @param url string类型的url
     * @param requestCode 请求代码的参数
     * @param callback 路由器回调
     * @return openForResult 通过url打开匹配的页面并回调
     * */
    public static boolean openForResult(Ability ability, String url, int requestCode, RouterCallback callback) {
        return openForResult(ability, Uri.parse(url), requestCode, callback);
    }
    /**
     * 打开匹配uri页面并回调结果
     *
     * @param ability 目标ability
     * @param uri 地址
     * @param requestCode 请求代码的参数
     * @return openForResult 打开匹配uri页面并回调结果
     * */
    public static boolean openForResult(Ability ability, Uri uri, int requestCode) {

        return openForResult(ability, uri, requestCode, getGlobalCallback(ability));

    }
    /**
     * 打开匹配的url页面并回调结果
     *
     * @param ability 目标ability
     * @param requestCode 请求代码的参数
     * @param uri 地址
     * @param callback 路由器回调
     * @return openForResult 打开匹配的url页面并回调结果
     * */
    public static boolean openForResult(Ability ability, Uri uri, int requestCode, RouterCallback callback) {

        return open(ability, uri, requestCode, callback);
    }

    private static boolean open(Context context, Uri uri, int requestCode, RouterCallback callback) {
        boolean success = false;

        if (callback != null) {
            if (callback.beforeOpen(context, uri)) {
                return false;
            }
        }

        try {
            success = doOpen(context, uri, requestCode);
        } catch (Throwable e) {
            e.printStackTrace();
            if (callback != null) {
                callback.error(context, uri, e);
            }
        }

        if (callback != null) {
            if (success) {
                callback.afterOpen(context, uri);
            } else {
                callback.notFound(context, uri);
            }
        }
        return success;
    }
    /**
     * resolve
     *
     * @param context 上下文
     * @param url 地址
     * @return intent 返回解决的方法
     * */
    public static Intent resolve(Context context, String url) {
        return resolve(context, Uri.parse(url));
    }
    /**
     * resolve
     *
     * @param context 上下文
     * @param uri 地址
     * @return intent 返回null的方法
     * */
    public static Intent resolve(Context context, Uri uri) {
        initIfNeed();
        Path path = Path.create(uri);
        for (Mapping mapping : mappings) {
            if (mapping.match(path)) {
                Intent intent2 = new Intent();
                Operation operation = new Intent.OperationBuilder()
                        .withBundleName(context.getBundleName())
                        .withAbilityName(mapping.getAbility())
                        .build();
                intent2.setParams(mapping.parseExtras(uri));
                intent2.setParam(KEY_RAW_URL,uri.toString());
                intent2.setOperation(operation);
                return intent2;
            }
        }
        return null;
    }

    private static boolean doOpen(Context context, Uri uri, int requestCode) {
        initIfNeed();
        Path path = Path.create(uri);
        for (Mapping mapping : mappings) {
            if (mapping.match(path)) {
                if (mapping.getAbility() == null) {
                    mapping.getMethod().invoke(context, mapping.parseExtras(uri));
                    return true;
                }
                Intent intent1 = new Intent();
                Operation operation = new Intent.OperationBuilder()
                        .withBundleName(context.getBundleName())
                        .withAbilityName(mapping.getAbility())
                        .build();
                intent1.setParams(mapping.parseExtras(uri));
                intent1.setParam(KEY_RAW_URL,uri.toString());
                intent1.setOperation(operation);

                if (!(context instanceof Ability)) {
                    intent1.addFlags(Intent.FLAG_ABILITY_NEW_MISSION);
                }
                if (requestCode >= 0) {
                    if (context instanceof Ability) {
                        ((Ability)context).startAbilityForResult(intent1,requestCode);
                    } else {
                        throw new RuntimeException("can not startAbilityForResult context " + context);
                    }
                } else {
                    context.startAbility(intent1,MARK);
                }
                return true;
            }
        }
        return false;
    }

    private static RouterCallback getGlobalCallback(Context context) {
        if (((Ability)context).getAbilityPackage() instanceof RouterCallbackProvider) {
            return ((RouterCallbackProvider) ((Ability)context).getAbilityPackage()).provideRouterCallback();
        }
        return null;
    }
}
